/* Copyright  2009-2010
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename: db_pthread.h
 */

/* Defines to make different thread packages compatible */

#ifndef __DB_PTHREAD_H__
#define __DB_PTHREAD_H__

#include <stdint.h>
#include <unistd.h>
#include <signal.h>

#if !defined(__cplusplus)
# include <stdbool.h>
#endif

#ifndef ETIME
#define ETIME ETIMEDOUT /* For FreeBSD */
#endif

#ifdef  __cplusplus
#define EXTERNC extern "C"
extern "C" {
#else
#define EXTERNC
#endif /* __cplusplus */

#include <pthread.h>

#ifdef HAVE_SCHED_H
#include <sched.h>
#endif
#ifdef HAVE_SYNCH_H
#include <synch.h>
#endif

#define pthread_key(T,V) pthread_key_t V
#define pthread_handler_t void *
typedef void *(* pthread_handler)(void *);

#if defined(HAVE_SIGTHREADMASK) && !defined(HAVE_PTHREAD_SIGMASK)
#define pthread_sigmask(A,B,C) sigthreadmask((A),(B),(C))
#endif


/*
  We define db_sigset() and use that instead of the system sigset() so that
  we can favor an implementation based on sigaction(). On some systems, such
  as Mac OS X, sigset() results in flags such as SA_RESTART being set, and
  we want to make sure that no such flags are set.
*/
#if defined(HAVE_SIGACTION) && !defined(db_sigset)
#define db_sigset(A,B) do { struct sigaction l_s; sigset_t l_set; int l_rc; \
                            assert((A) != 0);                               \
                            sigemptyset(&l_set);                            \
                            l_s.sa_handler = (B);                           \
                            l_s.sa_mask   = l_set;                          \
                            l_s.sa_flags   = 0;                             \
                            l_rc= sigaction((A), &l_s, (struct sigaction *) NULL);\
                            assert(l_rc == 0);                         \
                          } while (0)
#elif defined(HAVE_SIGSET) && !defined(db_sigset)
#define db_sigset(A,B) sigset((A),(B))
#elif !defined(db_sigset)
#define db_sigset(A,B) signal((A),(B))
#endif

#ifndef my_pthread_attr_setprio
#ifdef HAVE_PTHREAD_ATTR_SETPRIO
#define my_pthread_attr_setprio(A,B) pthread_attr_setprio((A),(B))
#else
extern void my_pthread_attr_setprio(pthread_attr_t *attr, int priority);
#endif
#endif

#if !defined(HAVE_PTHREAD_YIELD_ONE_ARG) &&!defined(HAVE_PTHREAD_YIELD_ZERO_ARG)
/* no pthread_yield() available */
#ifdef HAVE_SCHED_YIELD
#define pthread_yield() sched_yield()
#elif defined(HAVE_PTHREAD_YIELD_NP) /* can be Mac OS X */
#define pthread_yield() pthread_yield_np()
#endif
#endif

/*
  The defines set_timespec and set_timespec_nsec should be used
  for calculating an absolute time at which
  pthread_cond_timedwait should timeout
*/
#ifndef set_timespec
#define set_timespec(ABSTIME,SEC) \
{\
  struct timeval tv;\
  gettimeofday(&tv,0);\
  (ABSTIME).tv_sec=tv.tv_sec+(time_t) (SEC);\
  (ABSTIME).tv_nsec=tv.tv_usec*1000;\
}
#endif /* !set_timespec */
#ifndef set_timespec_nsec
#define set_timespec_nsec(ABSTIME,NSEC) \
{\
  uint64_t now= db_getsystime() + (NSEC/100); \
  (ABSTIME).tv_sec=  (time_t) (now / 10000000UL);                  \
  (ABSTIME).tv_nsec= (long) (now % 10000000UL * 100 + ((NSEC) % 100)); \
}
#endif /* !set_timespec_nsec */

	/* safe_mutex adds checking to mutex for easier debugging */

typedef struct st_safe_mutex_t
{
  pthread_mutex_t global,mutex;
  const char *file;
  uint32_t line,count;
  pthread_t thread;
} safe_mutex_t;

    /* Wrappers if safe mutex is actually used */
#define safe_mutex_assert_owner(mp)
#define safe_mutex_assert_not_owner(mp)

	/* READ-WRITE thread locking */

#ifndef HAVE_THR_SETCONCURRENCY
#define thr_setconcurrency(A) pthread_dummy(0)
#endif
#if !defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) && !defined(pthread_attr_setstacksize)
#define pthread_attr_setstacksize(A,B) pthread_dummy(0)
#endif

/* Define mutex types, see my_thr_init.c */
#define MY_MUTEX_INIT_SLOW   NULL
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
extern pthread_mutexattr_t  my_fast_mutexattr;
#define MY_MUTEX_INIT_FAST &my_fast_mutexattr
#else
#define MY_MUTEX_INIT_FAST   NULL
#endif
#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
extern pthread_mutexattr_t my_errorcheck_mutexattr;
#define MY_MUTEX_INIT_ERRCHK &my_errorcheck_mutexattr
#else
#define MY_MUTEX_INIT_ERRCHK   NULL
#endif

#ifndef ESRCH
/* Define it to something */
#define ESRCH 1
#endif

typedef uint64_t db_thread_id;

extern bool db_thread_global_init(void);
extern void db_thread_global_end(void);

extern bool db_thread_init(void);
extern void db_thread_end(void);

//extern const char * my_thread_name(void);
extern db_thread_id db_thread_dbug_id(void);

/* All thread specific variables are in the following struct */

#define THREAD_NAME_SIZE 10
/*
  Collapasr can survive with 32K, but some glibc libraries require > 128K stack
  to resolve hostnames. Also recursive stored procedures needs stack.
*/
#define DEFAULT_THREAD_STACK    (256*INT32_C(1024))

struct st_thread_var
{
  pthread_cond_t suspend;
  pthread_mutex_t mutex;
  pthread_mutex_t*  volatile current_mutex;
  pthread_cond_t *  volatile current_cond ;
  pthread_t                  pthread_self ;
  db_thread_id                         id ;
  int cmp_length;
  int volatile abort;
  bool init;
  struct st_thread_var *next,**prev;
  void *opt_info;
};

extern struct st_thread_var *_my_thread_var(void);
extern uint32_t my_thread_end_wait_time;
#define my_thread_var (_my_thread_var())
/*
  Keep track of shutdown,signal, and main threads so that my_end() will not
  report errors with them
*/

/* Which kind of thread library is in use */

#define THD_LIB_OTHER 1
#define THD_LIB_NPTL  2
#define THD_LIB_LT    4

extern uint32_t thd_lib_detected;

/*
  thread_safe_xxx functions are for critical statistic or counters.
  The implementation is guaranteed to be thread safe, on all platforms.
  Note that the calling code should *not* assume the counter is protected
  by the mutex given, as the implementation of these helpers may change
  to use my_atomic operations instead.
*/

#ifndef thread_safe_increment
#define thread_safe_increment(V,L) \
        (pthread_mutex_lock((L)), (V)++, pthread_mutex_unlock((L)))
#define thread_safe_decrement(V,L) \
        (pthread_mutex_lock((L)), (V)--, pthread_mutex_unlock((L)))
#endif

#ifndef thread_safe_add
#define thread_safe_add(V,C,L) \
        (pthread_mutex_lock((L)), (V)+=(C), pthread_mutex_unlock((L)))
#define thread_safe_sub(V,C,L) \
        (pthread_mutex_lock((L)), (V)-=(C), pthread_mutex_unlock((L)))
#endif

/*
  statistics_xxx functions are for non critical statistic,
  maintained in global variables.
  - race conditions can occur, making the result slightly inaccurate.
  - the lock given is not honored.
*/
#define statistic_decrement(V,L) (V)--
#define statistic_increment(V,L) (V)++
#define statistic_add(V,C,L)     (V)+=(C)
#define statistic_sub(V,C,L)     (V)-=(C)

/*
  No locking needed, the counter is owned by the thread
*/
#define status_var_increment(V) (V)++
#define status_var_decrement(V) (V)--
#define status_var_add(V,C)     (V)+=(C)
#define status_var_sub(V,C)     (V)-=(C)

#ifdef  __cplusplus
}
#endif
#endif /* __DB_PTHREAD_H__ */
